package com.qfmy.service.impl.novel;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.qfmy.config.DashScopeConfig;
import com.qfmy.mapper.novel.NovelMapper;
import com.qfmy.service.novel.NovelAgentService;
import common.exception.BaseException;
import common.login.LoginUser;
import common.login.LoginUserHolder;
import common.result.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import model.entity.novel.Novel;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author 清风明月
 * @Package com.qfmy.service.impl.novel
 * @date 2025/6/8
 * @description 智能小说
 */
@Slf4j
@Service
@SuppressWarnings("all")
public class NovelAgentServiceImpl implements NovelAgentService {

    /**
     * 注入小说mapper
     */
    @Autowired
    private NovelMapper novelMapper;

    /**
     * 注入redisTemplate
     */
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 注入disconfig
     */
    @Autowired
    private DashScopeConfig  dashScopeConfig;

    /**
     * 智能生成
     * */
    private Generation gen = new Generation();


    /**
     * 智能搜索
     * @param keyword
     * @return
     */
    @Override
    public String searchBook(String keyword) {
        //构建对话
        List<Message> messages = getMessage(keyword);
        //构建请求题
        // 构建请求参数
        GenerationParam param = GenerationParam.builder()
                .apiKey(dashScopeConfig.getApiKey())
                .model(dashScopeConfig.getModelNovel())
                .messages(messages)
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .build();
        try {
            return gen.call(param).getOutput().getChoices().get(0).getMessage().getContent();
        } catch (Exception e) {
            log.error("智能搜索异常：{}",e);
            throw new BaseException( 404,"智能搜索异常");
        }
    }

    /**
     * 清除缓存
     */
    @Override
    public void clearCache() {
        //获取用户id
        Long userId = getUserId();
        //创建缓存key
        String key = "user:" + userId + ":novel:agent:message";
        redisTemplate.delete(key);
        //获取小说数据
        List<Novel> novels = novelMapper.getAllNovel();
        //创建集合
        List<Message> messages = new ArrayList<>();
        String prompt = "你是一个专业的小说助手。现在，我从数据库中获取了小说信息，包括小说名称、作者、类型、状态（连载/完结）、是否免费、字数以及简要描述（变量名：novels ，包含完整小说数据）。请你严格按照以下逻辑，为用户提供帮助：\n" +
                "\n" +
                "### 1. 判断用户需求\n" +
                "   首先明确用户是想了解**单本小说**还是**多本小说**的推荐，同时抓取用户提到的偏好条件，比如类型（奇幻、玄幻等）、是否免费、连载/完结状态等。\n" +
                "\n" +
                "### 2. 执行小说推荐\n" +
                "   - 若用户需求为单本小说：从 novels 数据里筛选出**最贴合所有偏好条件**的 1 部，直接输出格式为「小说名称 - 作者 - 类型」，无需附加其他描述。\n" +
                "   - 若用户需求为多本小说：从 novels 数据里筛选出**符合偏好条件的多部**，每部均按「小说名称 - 作者 - 类型」格式罗列，清晰呈现。\n" +
                "\n" +
                "### 3. 核心原则\n" +
                "   语言务必简洁明了，只保留小说关键信息（名称、作者、类型 ），避免多余描述性内容，让用户快速获取所需推荐。请依据提供的 " +novels + "数据对用户进行回答。";

        Message systemMsg = Message.builder()
                .role(Role.SYSTEM.getValue())
                .content(prompt)
                .build();
        messages.add(systemMsg);
        //存放到缓存里面
        redisTemplate.opsForValue().set(key,messages,1, TimeUnit.DAYS);
    }

    /**
     * 构建对话
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private List<Message> getMessage(String keyword) {
        //获取用户id
        Long userId = getUserId();
        //创建缓存key
        String key = "user:" + userId + ":novel:agent:message";
        //从缓存里面获取数据
        List<Message> messages = (List<Message>) redisTemplate.opsForValue().get(key);
        if(messages != null && messages.size() > 0)
        {
            return messages;
        }
        //创建消息集合
        messages = new ArrayList<>();
        //没有数据从数据库获取数据
        List<Novel> novels = novelMapper.getAllNovel();
        //构建Ai对话
        String prompt =  "\"你是一个专业的小说助手。现在，我从数据库中获取了小说信息，包括小说名称、类型、状态（连载/完结）、是否免费、字数以及简要描述，但没有具体的章节内容。请你根据这些信息，为用户提供以下几方面的帮助：\n" +
                "\n" +
                "1. **小说概述**：\n" +
                "   - 提供每部小说的简要介绍，包括名称、类型、状态和字数。\n" +
                "   - 利用小说的描述，简明扼要地概括小说的核心内容和亮点。\n" +
                "\n" +
                "2. **推荐选择**：\n" +
                "   - 根据用户的需求（例如类型偏好、是否免费等），推荐适合的小说。\n" +
                "   - 如果用户需要，可以根据小说的状态（连载或完结）进一步筛选。\n" +
                "\n" +
                "3. **比较分析**：\n" +
                "   - 如果用户对多部小说感兴趣，可以帮助他们比较不同小说的特点。\n" +
                "   - 强调每部小说的独特之处和优势。\n" +
                "\n" +
                "4. **信息补充**：\n" +
                "   - 如果用户对某部小说感兴趣，但需要更多信息，可以引导他们寻找更多资源，例如小说的官方平台或社区。\n" +
                "\n" +
                "5. **个性化建议**：\n" +
                "   - 根据用户提供的偏好或历史阅读记录，提供个性化的小说推荐。\n" +
                "\n" +
                "你的回答应该简洁明了、重点突出，避免冗长和复杂的表述，确保用户能够快速获取他们需要的信息。这个就是我提供的小说数据" + novels + "请你根据我提供的数据对用户进行回答";
        Message systemMsg = Message.builder()
                .role(Role.SYSTEM.getValue())
                .content(prompt)
                .build();
        messages.add(systemMsg);
        //把他存放到缓存里面
        redisTemplate.opsForValue().set(key,messages,12, TimeUnit.HOURS);
        //构建用户消息
        Message userMsg = Message.builder()
                .role(Role.USER.getValue())
                .content(keyword)
                .build();
        messages.add(userMsg);
        return messages;
    }

    /**
     * 获取用户id
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private Long getUserId() {
        LoginUser loginUser = LoginUserHolder.getLoginUser();
        //判断是否存在
        if(loginUser == null)
        {
            throw new BaseException(ResultCodeEnum.TOKEN_INVALID);
        }
        return loginUser.getUserId();
    }
}
